#include <iostream>
#include <vector>
#include <unordered_set>
#include <queue>

using namespace std;

// 752.打开转盘锁
// 你有一个带有四个圆形拨轮的转盘锁。每个拨轮都有10个数字： '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' 。
// 每个拨轮可以自由旋转：例如把 '9' 变为 '0'，'0' 变为 '9' 。每次旋转都只能旋转一个拨轮的一位数字。
// 锁的初始数字为 '0000' ，一个代表四个拨轮的数字的字符串。
//列表 deadends 包含了一组死亡数字，一旦拨轮的数字和列表里的任何一个元素相同，这个锁将会被永久锁定，无法再被旋转。
//字符串 target 代表可以解锁的数字，你需要给出解锁需要的最小旋转次数，如果无论如何不能解锁，返回 -1 。

class Solution {
private:
    // 将s[j]向上拨动一次
    string plusOne(string s, int j) {
        char ch = s[j];
        if(ch == '9')
            s[j] = '0';
        else
            s[j] += 1;
        return s;
    }
    // 将s[j]向下拨动一次
    string minusOne(string s, int j) {
        char ch = s[j];
        if(ch == '0')
            s[j] = '9';
        else
            s[j] -= 1;
        return s;
    }
public:
    int openLock(vector<string>& deadends, string target) {
        // 记录需要跳过的死亡密码
        unordered_set<string> deads;
        for(string &s: deadends)
            deads.insert(s);
        // 记录已经穷举过的密码，防止走回头路
        unordered_set<string> visited;
        // 从起点开始启动BFS
        queue<string> q;
        q.push("0000");
        int step = 0;   // 步数

        while(!q.empty()) {
            size_t sz = q.size();
            for(size_t i = 0; i < sz; i++) {
                string cur = q.front(); q.pop();    // 出队
                // 判断是否达到终点
                if(deads.contains(cur))
                    continue;
                if(cur == target)
                    return step;

                // 将一个结点的相邻结点加入队列
                for(int j =0; j < 4; j++) {
                    string up = plusOne(cur,j);
                    if(!visited.contains(up)) {
                        q.push(up);
                        visited.insert(up);
                    }
                    string down = minusOne(cur,j);
                    if(!visited.contains(down)) {
                        q.push(down);
                        visited.insert(down);
                    }

                } // for
            } // for
            // 处理完一层，增加步数
            step++;
        }
        // 如果穷举完都没找到目标密码，那就是找不到了
        return -1;
    }
};
int main() {
    vector<string> deadends1({"0201","0101","0102","1212","2002"});
    string target1 = "0202";
    cout << Solution().openLock(deadends1,target1) << endl; // 6

    vector<string> deadends2({"8888"});
    string target2 = "0009";
    cout << Solution().openLock(deadends2,target2) << endl; // 1

    vector<string> deadends3({"8887","8889","8878","8898","8788","8988","7888","9888"});
    string target3 = "8888";
    cout << Solution().openLock(deadends3,target3) << endl; // -1
    return 0;
}
